use std::{collections::HashMap, sync::Mutex, ops::DerefMut};

use crate::gen::{entities::{Account, Restaurant, Rider, Order}, mini_mulazim::KeysRequest};

use super::{exts::GetEntityKey, structs::GenericEntityIndex};

/**
 * @author Dream-Lab مۇختەرجان مەخمۇت
 * @email ug-project@outlook.com
 * @create date 2022-07-24 15:08:16
 * @modify date 2022-07-24 15:08:16
 * @desc [description]
*/

pub struct DataContainer {
    pub users: Mutex<HashMap<String, Account>>,
    pub restaurants: Mutex<HashMap<String, Restaurant>>,
    pub riders: Mutex<HashMap<String, Rider>>,
    pub orders: Mutex<HashMap<String, Order>>,
    ///Key: User id, Value: Order id
    pub orders_of_user: Mutex<HashMap<String, GenericEntityIndex>>,
    ///Key: Rider id, Value: Order id
    pub orders_of_rider: Mutex<HashMap<String, GenericEntityIndex>>,
}

impl DataContainer {
    pub fn new() -> DataContainer {
        DataContainer { 
            users: Mutex::new(HashMap::new()),
            restaurants: Mutex::new(HashMap::new()),
            riders: Mutex::new(HashMap::new()),
            orders: Mutex::new(HashMap::new()),
            orders_of_user: Mutex::new(HashMap::new()),
            orders_of_rider: Mutex::new(HashMap::new()),
        }
    }

    pub fn get_keys_of<T>(&self, data: &Mutex<HashMap<String, T>>, request: &KeysRequest) -> Vec<String> {
        Vec::from_iter(data.lock().expect("Cannot acquire data lock").keys().skip(request.from.unwrap_or(0) as usize).take(request.count.unwrap_or(1000) as usize).cloned())
    }
}

impl DataContainer {
    pub fn get_data_by_id_of<T>(&self, data: &Mutex<HashMap<String, T>>, key: &String) -> Option<T> where T : Clone {
        data.lock().expect("Cannot acquire lock").get(key).cloned()
    }
    pub fn get_user_by_id(&self, id: &String) -> Option<Account> {
        self.get_data_by_id_of(&self.users, id)
    }
    pub fn get_restaurant_by_id(&self, id: &String) -> Option<Restaurant> {
        self.get_data_by_id_of(&self.restaurants, id)
    }
    pub fn get_rider_by_id(&self, id: &String) -> Option<Rider> {
        self.get_data_by_id_of(&self.riders, id)
    }
    pub fn get_order_by_id(&self, id: &String) -> Option<Order> {
        self.get_data_by_id_of(&self.orders, id)
    }
}

impl DataContainer {
    pub fn insert_data<ENTITY: GetEntityKey + Clone>(&self, data: &Mutex<HashMap<String, ENTITY>>, entity: ENTITY) -> ENTITY {
        data.lock().expect("Cannot acquire data lock").insert(entity.key().clone(), entity.clone());
        entity
    }
    pub fn insert_user(&self, user: Account) -> Account {
        self.insert_data(&self.users, user)
    }
    pub fn insert_restaurant(&self, restaurant: Restaurant) -> Restaurant {
        self.insert_data(&self.restaurants, restaurant)
    }
    pub fn insert_rider(&self, rider: Rider) -> Rider {
        self.insert_data(&self.riders, rider)
    }pub fn insert_order(&self, order: Order) -> Order {
        self.insert_data(&self.orders, order)
    }
}

impl DataContainer {
    pub fn update_data<ENTITY: GetEntityKey + Clone>(&self, data: &Mutex<HashMap<String, ENTITY>>, entity: ENTITY) -> ENTITY {
        self.insert_data(data, entity)
    }
    pub fn update_user(&self, user: Account) -> Account {
        self.update_data(&self.users, user)
    }
    pub fn update_restaurant(&self, restaurant: Restaurant) -> Restaurant {
        self.update_data(&self.restaurants, restaurant)
    }
    pub fn update_rider(&self, rider: Rider) -> Rider {
        self.update_data(&self.riders, rider)
    }pub fn update_order(&self, order: Order) -> Order {
        self.update_data(&self.orders, order)
    }
}